/* 
 * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "ResourceManager.h"

#include "User.h"

#include "Client.h"
#include "FavoriteUser.h"

#include "ConnectionManager.h"
#include "ClientManager.h"
// CDM EXTENSION BEGINS
#include "ClientProfileManager.h"
#include "QueueManager.h"
#include "DebugManager.h"
#include "SettingsManager.h"
#include "StringTokenizer.h"
#include "LogManager.h"
// CDM EXTENSION ENDS
//Sulan start svn 163
#include "RegexpHandler.h"
//END
// Pothead starts svn196 (sulan code)
#include "MyinfoDetect.h"
// pothead ends

User::~User() throw() {
	delete favoriteUser;
}

void User::connect() {
	RLock l(cs);
	if(client) {
		if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
			client->connectToMe(this);
		} else {
			client->revConnectToMe(this);
		}
	}
}

const string& User::getClientNick() const {
	RLock l(cs);
	if(client) {
		return client->getNick();
	} else {
		return SETTING(NICK);
	}
}

void User::updated(User::Ptr& aUser) {
	RLock l(aUser->cs);
	if(aUser->client) {
		aUser->client->updated(aUser);
	}
}

const string& User::getClientName() const {
	RLock l(cs);
	if(client) {
		return client->getName();
	} else if(!getLastHubName().empty()) {
		return getLastHubName();
	} else {
		return STRING(OFFLINE);
	}
}

string User::getClientAddressPort() const {
	RLock l(cs);
	if(client) {
		return client->getAddressPort();
	} else {
		return Util::emptyString;
	}
}

string User::getClientRandomNick() const {
	RLock l(cs);
	if(client) {
		return client->getRandomNick();
	} else {
		return Util::emptyString;
	}
}

void User::privateMessage(const string& aMsg) {
	RLock l(cs);
	if(client) {
		client->privateMessage(this, aMsg);
	}
}

bool User::isClientOp() const {
	RLock l(cs);
	if(client) {
		return client->getOp();
	}
	return false;
}

void User::kick(const string& aMsg) {
	RLock l(cs);
	if(client) {
		client->kick(this, aMsg);
	}
}

void User::send(const string& aMsg) {
	RLock l(cs);
	if(client) {
		client->send(aMsg);
	}
}

void User::redirect(const string& aTarget, const string& aReason) {
	RLock l(cs);
	if(client) {
		client->opForceMove(this, aTarget, aReason);
	}
}

void User::clientMessage(const string& aMsg) {
	RLock l(cs);
	if(client) {
		client->sendMessage(aMsg);
	}
}

void User::clientPM(const string& aTo, const string& aMsg) {
	RLock l(cs);
	if(client) {
		client->privateMessage(aTo, aMsg);
	}
}

void User::clientSend(const string& aRaw) {
	RLock l(cs);
	if(client) {
		client->sendRaw(aRaw);
	}
}

void User::setClient(Client* aClient) { 
	WLock l(cs); 
	client = aClient; 
	if(client == NULL) {
		if (isSet(ONLINE) && isFavoriteUser())
			setFavoriteLastSeen();
		unsetFlag(ONLINE);
	}
	else {
		setLastHubAddress(aClient->getIpPort());
		setLastHubName(aClient->getName());
		setFlag(ONLINE);
		unsetFlag(QUIT_HUB);
	}
};

// CDM EXTENSION BEGINS
//void User::getParams(StringMap& ucParams);
void User::getParams(StringMap& ucParams, bool myNick /* = false */) {
// CDM EXTENSION ENDS
	ucParams["nick"] = getNick();
	ucParams["tag"] = getTag();
	ucParams["description"] = getDescription();
	ucParams["email"] = getEmail();
	ucParams["share"] = Util::toString(getBytesShared());
	ucParams["shareshort"] = Util::formatBytes(getBytesShared());
	ucParams["ip"] = getIp();
	// CDM EXTENSION BEGINS
	ucParams["userip"] = getUserIp();
	ucParams["tag"] = getTag();
	ucParams["clienttype"] = getClientType();
	ucParams["statedshare"] = Util::toString(getBytesShared());
	ucParams["statedshareformat"] = Util::formatBytes(getBytesShared());
	if(realShare > -1) {
		ucParams["realshare"] = Util::toString(realShare);
		ucParams["realshareformat"] = Util::formatBytes(realShare);
	}
	if(filesShared > -1) {
		ucParams["filesshared"] = Util::toString(filesShared);
	}
	ucParams["cheatingdescription"] = changeCheatingDescriptionsTokens();
	ucParams["cd"] = changeCheatingDescriptionsTokens();
	ucParams["clientinfo"] = getReport();
	ucParams["nl"] = "\r\n";
	ucParams["adl"] = adlsComment;
	if(myNick && client) {
		RLock l(cs);
		ucParams["mynick"] = client->getNick();
	}
	//Sulan fix
	ucParams["ct"] = clientType;
	ucParams["cc"] = clientComment;
	//Sulan start 28
	ucParams["host"] = host;
	if(client) {
		RLock l(cs);
		ucParams["t"] = client->getTrigger();
		ucParams["opchat"] = client->getOpChat();
		// http://www.imperialnet.org/forum/viewtopic.php?t=1313
	// pothead starts svn192 svn218
		ucParams["hubmessage"] = client->getHubMessage();
	}
	//End
	ucParams["connection"] = getConnection();
	ucParams["adlfile"] = adlFile;
	ucParams["adlfilesize"] = adlFileSize;
	ucParams["adltth"] = adlTTH;
	ucParams["forbiddensize"] = Util::formatBytes(adlForbiddenSize);
	// CDM EXTENSION ENDS
}

// favorite user stuff
void User::setFavoriteUser(FavoriteUser* aUser) {
	WLock l(cs);
	delete favoriteUser;
	favoriteUser = aUser;
	// CDM EXTENSION BEGINS
	protectedUser = true;
	// CDM EXTENSION ENDS
}

bool User::isFavoriteUser() const {
	RLock l(cs);
	return (favoriteUser != NULL);
}

bool User::getFavoriteGrantSlot() const {
	RLock l(cs);
	return (favoriteUser != NULL && favoriteUser->isSet(FavoriteUser::FLAG_GRANTSLOT));
}

void User::setFavoriteGrantSlot(bool grant) {
	WLock l(cs);
	if (favoriteUser == NULL)
		return;

	if (grant)
		favoriteUser->setFlag(FavoriteUser::FLAG_GRANTSLOT);
	else
		favoriteUser->unsetFlag(FavoriteUser::FLAG_GRANTSLOT);
}

void User::setFavoriteLastSeen(u_int32_t anOfflineTime) {
	WLock l(cs);
	if (favoriteUser != NULL) {
		if (anOfflineTime != 0)
			favoriteUser->setLastSeen(anOfflineTime);
		else
			favoriteUser->setLastSeen(GET_TIME());
	}
}

u_int32_t User::getFavoriteLastSeen() const {
	RLock l(cs);
	if (favoriteUser != NULL)
		return favoriteUser->getLastSeen();
	else
		return 0;
}

const string& User::getUserDescription() const {
	RLock l(cs);
	if (favoriteUser != NULL)
		return favoriteUser->getDescription();
	else
		return Util::emptyString;
}

void User::setUserDescription(const string& aDescription) {
	WLock l(cs);
	if (favoriteUser != NULL)
		favoriteUser->setDescription(aDescription);
}
// CDM EXTENSION BEGINS 
string User::getReport() {
	string temp = Util::formatBytes(downloadSpeed);
	// Pothead extension starts, changed strings to stringdefs #10 svn34
	string report = (clientType.empty()) ? "\r\n" + STRING(CLIENT) + ":		N/A" : string("\r\n" + STRING(CLIENT) + ":		" + clientType + "\t[" + Util::formatTime("%d-%m %H:%M", testsurCheckedAt) + ']');
	report += "\r\n" + STRING(FILELIST_GENERATOR) + ":	" + filelistGenerator;
	report += "\r\n" + STRING(LOCK) + ":		" + lock;
	report += "\r\n" + STRING(PK) + ":		" + pk;
	report += "\r\n" + STRING(TAG) + ":		" + tag;
	report += "\r\n" + STRING(SUPPORTS) + ":		" + supports;
	report += "\r\n" + STRING(STATUS) + ":		" + Util::formatStatus(iconStatus);
	report += "\r\n" + STRING(TESTSUR) + ":		" + testSUR;
	report += "\r\n" + STRING(DISCONNECTS) + ":	" + Util::toString(fileListDisconnects);
	report += "\r\n" + STRING(TIMEOUTS) + ":		" + Util::toString(connectionTimeouts);
	if (temp.empty()) {
		temp = "N/A";
	} else {
		temp += "/s";
	}
	report += "\r\n" + STRING(DOWNSPEED) + ":	" + temp;
	report += "\r\n" + STRING(SETTINGS_IP) + ":		" + ip;
	report += "\r\n" + STRING(USER_IP) + ":		" + userIp;
	report += "\r\n" + STRING(SEARCHPORT) + "	" + Util::toString(searchPort);
	report += "\r\n" + STRING(CID) + ":		" + cid;
	report += "\r\n" + STRING(ISP) + ":		" + isp;
	report += "\r\n" + STRING(KNOWNHUBS) + ":	" + Util::toString(connectedHubs) + " " + knownHubsString;
	report += "\r\n" + STRING(STATEDHUBS) + ":	" + getTotalHubs();
	report += "\r\n" + STRING(HOST) + ":		" + host;
	report += "\r\n" + STRING(DESCRIPTION) + ":	" + description;
	report += "\r\n" + STRING(EMAIL) + ":		" + email;
	report += "\r\n" + STRING(CONNECTION) + ":	" + connection;
	report += "\r\n" + STRING(COMMANDS) + ":	" + unknownCommand;
	//Sulan start svn94
	if(myinfos > 0 && (GET_TICK() - loggedIn)>0 ){
			report += "\r\n" + STRING(MYINFOS) + "\t[ " + Util::toString(myinfos) + " ] interval: [ " + Util::formatSeconds(((GET_TICK() - loggedIn)/1000) / myinfos) + " ]";
	}else{
		report += "\r\n" + STRING(MYINFOS) + "\t[ 0 ] interval: [ 0 ]";
	}
	//End

	temp = (getFileListSize() != -1) ? Util::formatBytes(fileListSize) + " (" + Util::toString(fileListSize) + " B)" : string("N/A");
	report += "\r\n" + STRING(FILELISTSIZE) + ":	" + temp;
	if ( listLength != -1 ) {
		temp = Util::formatBytes(listLength) + " (" + Util::toString(listLength) + " B)";
	} else {
		temp = "N/A";
	}
	report += "\r\n" + STRING(LISTLEN) + ":		" + temp;
	report += "\r\n" + STRING(STATEDSHARE) + ":	" + Util::formatBytes(bytesShared) + " (" + Util::toString(bytesShared) + " B)";
	temp = (getRealShare() != -1) ? Util::formatBytes(realShare) + " (" + Util::toString(realShare) + " B)" + "\t[" + Util::formatTime("%d-%m %H:%M", fileListCheckedAt) + ']' : string("N/A");
	report += "\r\n" + STRING(REALSHARE) + ":	" + temp;
	if(filesShared != -1) {
		temp = (hashedFilesShared != -1) ? Util::toString(filesShared) + " (hashed: " + Util::toString(hashedFilesShared) + ')' : Util::toString(filesShared);
	} else {
		temp = "N/A";
	}
	//temp = (filesShared != -1) ? Util::toString(filesShared) : "N/A";
	report += "\r\n" + STRING(TOTALFILES) + ":	" + temp;
	report += "\r\n" + STRING(PERCENTAGEHASHED) + ":	" + percentHashed;
	report += "\r\n" + STRING(TOTALPOINTS) + ":	" + Util::toString(totalPoints);

	StringTokenizer<string> st(cheatingString, ';');
	StringList& sl = st.getTokens();
	temp = "";
	for(StringIter i = sl.begin(); i != sl.end(); ++i) {
		temp += *i + "\n\t\t";
	}
	if (temp.empty()) { temp = STRING(NA); }
	report += "\r\n" + STRING(CHEATSTATUS) + ":	" + temp;
	report += "\r\n" + STRING(COMMENT) + ":		" + clientComment;
	report += "\r\n" + STRING(LOGGEDIN) + ":	" + loginTime + " (" + Util::formatSeconds((GET_TICK() - loggedIn) / 1000) + ")";
#ifdef _DEBUG
	report += "\r\n" + STRING(DEBUGINFO) + ":\r\n" + STRING(FILELISTCOMPLETE) + ":		" + Util::toString(filelistComplete);
	report += "\r\n" + STRING(HASFILELISTINQUEUE) + ":		" + Util::toString(hasFilelistinQueue);
	report += "\r\n" + STRING(TESTSURCOMPLETE) + ":		" + Util::toString(testSURComplete);
	report += "\r\n" + STRING(TESTSURONICKCOMPLETE) + ":	" + Util::toString(testSURoNickComplete);
//	report += "\r\nshouldCheckFilelist()	" + Util::toString(shouldCheckFilelist());
	// pothead extension ends
#endif
	return report;
}

bool User::CDMsCureCancerAndMakeYourDickBigger() {
	setHasTestSURinQueue(false);
	if (!protectedUser){
		if ( getCountryCodeCheck()){
			logDetect(true);
			return false;
		}
	}
	if (isSet(User::DCPLUSPLUS) && (listLength == 11) && (bytesShared > 0)) {
		//Sulan fix
		if (getClaimsToBeDCPP()) {
			if (Util::toFloat(getTagVersion()) < 0.4033 || filelistComplete) {
			//End
			// http://www.imperialnet.org/forum/viewtopic.php?t=1555
				// pothead starts svn61 (stringdefs and display cheat)
				setCheat(STRING(FAKE_FILELIST_AND_LISTLEN) + " = 11B" , true, true);
				clientType = "DC++ Stealth";
				if (BOOLSETTING(DISPLAY_LISTLEN_ELEVEN_CHEAT))
					displayCheat(nick + " " + STRING(FAKE_FILELIST_AND_LISTLEN) + " = 11B");
				// pothead end
				logDetect(true);
				sendRawCommand(SETTING(LISTLEN_ELEVEN), Client::CONTEXT_RAW_FAKER);
				return false;
			} else if (SETTING(RECHECK_LISTLEN_11B_FAILED_AFTER_FILELIST)){
				failedListlen11BWantRecheck = true;
			}	
		}
	}
	if(isHubCountFaking()) {
		logDetect(true);
		return false;
	}
	int64_t tick = GET_TICK();

	StringMap params;
	ClientProfile::List& lst = ClientProfileManager::getInstance()->getClientProfiles(params);

	for(ClientProfile::Iter i = lst.begin(); i != lst.end(); ++i) {
		ClientProfile& cp = *i;	
		string version, pkVersion, extraVersion, formattedTagExp, verTagExp;

		verTagExp = RegexpHandler::formatRegExp(cp.getTag(), params);

		formattedTagExp = verTagExp;
		string::size_type j = formattedTagExp.find("%[version]");
		if(j != string::npos) {
			formattedTagExp.replace(j, 10, ".*");
		}

		string pkExp = cp.getPk();
		string formattedPkExp = pkExp;
		j = pkExp.find("%[version]");
		if(j != string::npos) {
			formattedPkExp.replace(j, 10, ".*");
		}
		string extTagExp = cp.getExtendedTag();
		string formattedExtTagExp = extTagExp;
		j = extTagExp.find("%[version2]");
		if(j != string::npos) {
			formattedExtTagExp.replace(j, 11, ".*");
		}

		DETECTION_DEBUG("\tChecking profile: " + cp.getName());

		if (!RegexpHandler::matchProfile(lock, cp.getLock()))							{ continue; }
		if (!RegexpHandler::matchProfile(tag, formattedTagExp))							{ continue; } 
		if (!RegexpHandler::matchProfile(pk, formattedPkExp))							{ continue; }
		if (!RegexpHandler::matchProfile(supports, cp.getSupports()))					{ continue; }
		if (!RegexpHandler::matchProfile(testSUR, cp.getTestSUR()))						{ continue; }
		if (!RegexpHandler::matchProfile(Util::toString(iconStatus), cp.getStatus()))	{ continue; }
		if (!RegexpHandler::matchProfile(unknownCommand, cp.getUserConCom()))			{ continue; }
		if (!RegexpHandler::matchProfile(description, formattedExtTagExp))				{ continue; }
		if (!RegexpHandler::matchProfile(connection, cp.getConnection()))				{ continue; }

		if (verTagExp.find("%[version]") != string::npos) { version = RegexpHandler::getVersion(verTagExp, tag); }
		if (extTagExp.find("%[version2]") != string::npos) { extraVersion = RegexpHandler::getVersion(extTagExp, description); }
		if (pkExp.find("%[version]") != string::npos) { pkVersion = RegexpHandler::getVersion(pkExp, getPk()); }

		if (!(cp.getVersion().empty()) && !RegexpHandler::matchProfile(version, cp.getVersion()))	{ continue; }

		DETECTION_DEBUG("Client found: " + cp.getName() + " time taken: " + Util::toString(GET_TICK()-tick) + " milliseconds");
		setClientType(cp.getName());
		if (cp.getUseExtraVersion()) {
			if (extraVersion != "")
				setClientType(cp.getName() + " " + extraVersion );
		} else {
			if (version != "")
			setClientType(cp.getName() + " " + version);
		}
		
		clientComment = cp.getComment();

		if (!getCheatFromProfiles().empty()) {
			sortCheatingString(getCheatFromProfiles(), false);
			setCheatFromProfiles(Util::emptyString);
		}

		if (!cp.getCheatingDescription().empty()) {
			setCheatFromProfiles(cp.getCheatingDescription());
			setCheat(cp.getCheatingDescription(), true, false);
		}
		logDetect(true);

		if (cp.getCheckMismatch() && version.compare(pkVersion) != 0) { 
			clientType += (" " + STRING(VERSION_MISMATCH));
			if (BOOLSETTING(DISPLAY_VERSION_MISMATCH_CHEAT))
				displayCheat(nick + " " + STRING(VERSION_MISMATCH));
			setCheat("Version mis-match", true, false);
			updated();
			sendRawCommand(SETTING(VERSION_MISMATCH), Client::CONTEXT_RAW_FAKER);
			return false;
		}
		updated();
		if(cp.getRawToSend() > 0 && (rechecked || !cp.getRecheck())) {
			sendRawCommand(cp.getRawToSend(), Client::CONTEXT_RAW_CLIENT);
		}
		skipExtended = cp.getSkipExtended();
		skipExtended2 = cp.getSkipExtended2();
		return cp.getRecheck() ? shouldRecheck() : false;
	}
	logDetect(false);
	setClientType("Unknown");
	//Dont know if this is a good idea
	//Sulan fix
	clientComment = Util::emptyString;
	//End
	// http://www.imperialnet.org/forum/viewtopic.php?t=1313
	badClient = false;
	updated();
	return false;
}

//Sulan start
void User::checkFilelistGenerator(const string& listGen, const string& aCID, const string& aBase) {

	filelistGenerator = listGen;
	cid = aCID;
	base = aBase;

	if(Util::toFloat(getTagVersion()) > 0.668 && getClaimsToBeDCPP()) {
		if(aCID.empty()||aBase.empty()){
			// pothead starts svn61 (stringdefs and display cheat)
			setCheat(STRING(FILELIST_MISMATCH), true, false);
			displayCheat(nick + " " + STRING(FILELIST_MISMATCH));
			// pothead end
			if(SETTING(FAKE_FILE_LIST_RAW))
				sendRawCommand(SETTING(FAKE_FILE_LIST_RAW), Client::CONTEXT_RAW_FILELIST);
			updated();
			return;
		}
	} else {
		if((!aCID.empty() || !aBase.empty()) && getClaimsToBeDCPP()) {
			// For BigMuscle, i had to make one of the more stupid detect public ;-)
			// http://strongdc.berlios.de/forum/viewtopic.php?t=2215&postdays=0&postorder=asc&start=30
			//Sulan
			// pothead starts svn61 (stringdefs and display cheat)
			setCheat(STRING(FILELIST_MISMATCH), true, false);
			displayCheat(nick + " " + STRING(FILELIST_MISMATCH));
			// pothead end
			if(SETTING(FAKE_FILE_LIST_RAW))
				sendRawCommand(SETTING(FAKE_FILE_LIST_RAW), Client::CONTEXT_RAW_FILELIST);
			updated();
			return;
		}
	}

	//End

	int64_t tick = GET_TICK();
	FileListDetectorProfile::List& lst = ClientProfileManager::getInstance()->getFileListDetectors();

	DETECTION_DEBUG("Local:	\tChecking filelist profile, user: " + nick);

	for(FileListDetectorProfile::Iter i = lst.begin(); i != lst.end(); ++i) {
		FileListDetectorProfile& fd = *i;	
		
		if (!RegexpHandler::matchProfile(listGen, fd.getName())){ continue; }
		
		DETECTION_DEBUG("Filelistgenerator matched: " + fd.getName() + " time taken: " + Util::toString(GET_TICK()-tick) + " milliseconds");
		
		//Sulan start svn120
		if(!fd.getClient().empty()){
			setClientType(fd.getClient());
		}
		//End
		logDetect(true);
		if (!fd.getCheatingDescription().empty()) {
			setCheat(fd.getCheatingDescription(), false, true);
			updated();
			// pothead start svn61 (display cheat)
			displayCheat(nick + " " + fd.getCheatingDescription());
			// pothead end
			if(fd.getRawToSend() > 0 ) {
				sendRawCommand(fd.getRawToSend(), Client::CONTEXT_RAW_FILELIST);
			}
			return;
		}
		updated();
		return;
	}
	logDetect(false);
	//Sulan Fix
	//http://www.imperialnet.org/forum/viewtopic.php?t=1399
	updated();
	return;
}

void User::sendRawCommand(const int aRawCommand, const int context) {
	if(client) {
		//Sulan start svn129
		string raw;
		{
			RLock l(cs);
			raw = client->getRawCommand(aRawCommand);
		}	
		sendRawCommand(raw, context);
		//End
	}
}

string User::getRawCommand(const int aRawCommand) {
	RLock l(cs);
	if(client) {
		return client->getRawCommand(aRawCommand);
	}
	return Util::emptyString;
}

void User::sendRawCommand(const string& aRawCommand, const int context, bool formatted) {
	if (client) {
		if (client->getDisableRaws(context)) {
			return;
		}
	}
	// pothead fix start #2
	//if (client && !aRawCommand.empty() && !protectedUser) {
	if (client && !aRawCommand.empty() && !getProtected() && client->getOp()) {
	// pothead fix end
		SettingsManager::getInstance()->set(SettingsManager::TOTAL_RAW_COMMANDS_SENT, SETTING(TOTAL_RAW_COMMANDS_SENT) + 1);
		if(formatted) {
			if(BOOLSETTING(LOG_RAW_COMMANDS)) { logRawCommand(aRawCommand); }
			{
				RLock l(cs);
				client->sendRaw(aRawCommand);
			}
			return;
		}
		StringMap paramMap;
		getParams(paramMap);
		if(client) {
			RLock l(cs);
			paramMap["mynick"] = client->getNick();
		}
		string rawCommand = Util::formatParams(aRawCommand, paramMap);
		if(BOOLSETTING(LOG_RAW_COMMANDS)) { logRawCommand(rawCommand); }
		{
			RLock l(cs);
			client->sendRaw(rawCommand);
		}
	}
}

void User::logRawCommand(const string& aRawCommand) {
	StringMap logParams;
	getParams(logParams);
	logParams["raw"] = aRawCommand;
	if(client) {
		RLock l(cs);
		logParams["hub"] = client->getAddressPort();
	}
	LOGDT("rawCommands", Util::formatParams(SETTING(LOG_FORMAT_POST_RAW_COMMANDS), logParams));
}

void User::updated() {
	RLock l(cs);
	if(client) {
		User::Ptr user = this;
		client->updated(user);
	}
}
void User::setFileListShared(const int64_t aShareSize, const int aNumberOfFiles) {
	setRealShare(aShareSize);
	setFilesShared(aNumberOfFiles);
}
void User::addLine(const string& aLine) {
	RLock l(cs);
	if(client) {
		client->addLine(aLine);
	}
}
bool User::isCheckable(bool delay) {
	if(getProtected()) { 
		return false;
	}
	return !connection.empty() && (!delay || (GET_TICK() - loggedIn) > SETTING(CHECK_DELAY));
}
void User::setTestSURComplete() {
	testSURComplete = true;
	// pothead starts svn119
	unsetFlag(M_NICK);
	// pothead ends
	testsurCheckedAt = GET_TIME();
	if (failedListlen11BWantRecheck && filelistComplete)
		failedListlen11BWantRecheck = false;
}
void User::setTestSURoNickComplete() {
	testSURoNickComplete = true;
	unsetFlag(O_NICK);
	testsurCheckedAt = GET_TIME();
}
void User::setTestSURrNickComplete() {
	testSURrNickComplete = true;
	unsetFlag(R_NICK);
	testsurCheckedAt = GET_TIME();
}
void User::setFilelistComplete() {
	filelistComplete = true;
	hasFilelistinQueue = false;
	fileListCheckedAt = GET_TIME();
	if (failedListlen11BWantRecheck)
		testSURoNickComplete = false;
}
// pothead starts svn183 svn219
void User::setShouldRecheckFilelistBecauseOfADLSearch() {
	unsetFlag(User::FAILED_ADLSEARCH);
	filelistComplete = false;
	totalPoints = 0;
	adlFile = Util::emptyString;
	adlFileSize = Util::emptyString;
	adlTTH = Util::emptyString;
	adlForbiddenSize = 0;
	adlsComment = Util::emptyString;
	sortCheatingString(STRING(FORBIDDEN_FILES_INCLUDING), false);
}
// pothead ends
bool User::shouldCheckFilelist(bool checkClients, bool extendedCheck1, bool extendedCheck2) {
	if(checkClients && ((extendedCheck1 ? testSURoNickComplete : true) &&(extendedCheck2 ? testSURrNickComplete : true)) ){
		return !hasFilelistinQueue && !filelistComplete && testSURComplete && (GET_TIME() - testsurCheckedAt > 10);
	} else {
		return !hasFilelistinQueue && !filelistComplete;
	}
}
bool User::isMyInfoSpamming() {
	// Pothead starts svn196 (sulan code)
	if(client && client->getUseMyinfoDetect()) myinfoDetect();
	// pothead ends
	//Sulan start svn94
	myinfos++;
	u_int32_t now = GET_TICK();
	int i = now - lastMyInfo;
	if(i < SETTING(FLOOD_TIME)) {
		floodCounter++;
	} else {
		floodCounter = 0;
	}
	lastMyInfo = now;
	if(floodCounter > SETTING(FLOOD_COUNTER)) {
		floodCounter = 0;
		return true;
	}
	return false;
}
void User::setUserIp(const string& aIp) {
	if(aIp == userIp) return;
	userIp = aIp;
	gotIp(true);
	//setCountry();
};
bool User::getProtected() {
	RLock l(cs);
	string protectList = SETTING(PROTECTED_USERS);
	// pothead starts svn138
	//if(isSet(User::OP)||isFavoriteUser()){
	if(isSet(User::OP)||(BOOLSETTING(PROTECT_FAVOURITES) && isFavoriteUser())){
	// pothead ends
		protectedUser = true;
		return true;
	}
	if(client){
		//Sulan start 29
		if(!protectList.empty() && !client->getProtectedUsers().empty())
			protectList += " ";
		protectList += client->getProtectedUsers(); 
		//end
	}
	
	StringTokenizer<string> st(protectList, ' ');
	StringList& sl = st.getTokens();
	for(StringIter i = sl.begin(); i != sl.end(); ++i) {
		if (RegexpHandler::matchProfile(nick, *i)) { 
			protectedUser = true;
			return true; 
		}
	}
	return false;
}

bool User::getCountryCodeCheck() {
	RLock l(cs);

	if(client){
		if(!client->getCountryCodeCheck().empty()&& !getCountryCode().empty()){
			if(!protectedUser && RegexpHandler::matchProfile(getCountryCode(), client->getCountryCodeCheck())){
				setCheat(STRING(BAD_COUNTRY) , true, false);
				updated();
				if(client->getCountryCodeRaw() > 0 ) {
					sendRawCommand(client->getCountryCodeRaw(),Client::CONTEXT_RAW_BAD_ISP);
				}
				return true;
			}
		}
	}
	return false;
}

const string& User::getTagSlots() {
	string::size_type i = tag.rfind("S:");
	if(i != string::npos) {
		string::size_type j = tag.find_first_of(",>", i);
		i += 2;
		if(j != string::npos) {
			tagSlots = tag.substr(i, j-i);
		} else {
			tagSlots = tag.substr(i, 1);
		}
	} else {
		tagSlots = Util::emptyString;
	}
	return tagSlots;
}

// pothead starts svn195
// ugly as hell, but it works for now
const string& User::getLimitSpeed() {
	string::size_type i = tag.rfind("B:");
	if (i == string::npos) {
		i = tag.rfind("L:");
	}
	if (i == string::npos) {
		i = tag.rfind("U:");
	}
	if(i != string::npos) {
		string::size_type j = tag.find_first_of(",>", i);
		i += 2;
		if(j != string::npos) {
			limitSpeed = tag.substr(i, j-i);
		} else {
			limitSpeed = tag.substr(i, 1);
		}
	} else {
		limitSpeed = Util::emptyString;
	}
	return limitSpeed;
}
// pothead ends

string User::getHubs() {
	string::size_type i = tag.rfind("H:");
	if(i != string::npos) {
		string::size_type j = tag.find_first_of(",>", i);
		i += 2;
		if(j != string::npos) {
			return tag.substr(i, j-i);
		} else {
			return tag.substr(i, 1);
		}
	} else {
		return Util::emptyString;
	}
}

string User::getNormalHubs() {
	string s = getHubs();
	if(s != Util::emptyString) {
		string::size_type i = s.find("/");
		if(i != string::npos) {
			return s.substr(0, i);
		} else {
			return s;
		}
	} else {
		return s;
	}
}

string User::getRegisteredHubs() {
	string s = getHubs();
	if(s != Util::emptyString) {
		string::size_type i = s.find("/");
		if(i != string::npos) {
			i += 1;
			string::size_type j = s.find("/", i);
			if(j != string::npos) {
				return s.substr(i, j-i);
			} else {
				return s.substr(i);
			}
		} else {
			return s;
		}
	} else {
		return s;
	}
}

string User::getOpHubs() {
	string s = getHubs();
	if(s != Util::emptyString) {
		string::size_type i = s.rfind("/");
		if(i != string::npos) {
			i += 1;
			return s.substr(i);
		} else {
			return s;
		}
	} else {
		return s;
	}
}

int User::getTotalHubsAsInt() {
	if (getTag().rfind("/") == string::npos) { 
		return Util::toInt(getHubs()); 
	}
	int totalHubs = -1;
	totalHubs += Util::toInt(getNormalHubs());
	totalHubs += Util::toInt(getRegisteredHubs());
	totalHubs += Util::toInt(getOpHubs());
	if(totalHubs > -1) {
		totalHubs++;
	}
	return totalHubs;
}

string User::getTotalHubs() {
	int totalHubs = getTotalHubsAsInt();
	if(totalHubs > -1) {
		return Util::toString(totalHubs);
	} else {
		return Util::emptyString;
	}
}
bool User::isHubCountFaking() {
	if(!BOOLSETTING(AUTOMATICALLY_CHECK_HUB_COUNTS)) { return false; }
	if(clientType.compare("Fake hub count DC++") == 0) { return true; }
	int th = getTotalHubsAsInt();
	if(th > 0) {
		User::Ptr user = this;
		return ClientManager::getInstance()->isHubCountFaking(user, th);
	}
	return false;
}
void User::setFakeHubCounts(const int hubs, const string& hubString) {
	connectedHubs = hubs;
	knownHubsString = hubString;
	// Pothead start svn61 (display cheat and stringdefs changes)
	displayCheat( nick + " " + STRING(TAG_STATES) + " " + getTotalHubs() + " " + STRING(HUB) + "(s), " + STRING(REALLY_CONNECTED_TO) + " " + Util::toString(hubs) + " " + STRING(HUBS) + ".");
	setCheat(STRING(TAG_STATES) + " " + getTotalHubs() + " " + STRING(HUB) + "(s), " + STRING(REALLY_CONNECTED_TO) + " " + Util::toString(hubs) + " " + STRING(HUBS) + ".", true, false);
	// pothead end
	clientType = STRING(FAKE_HUB_COUNT_DC);
	badClient = true;
	updated();
}
bool User::fileListDisconnected() {
	fileListDisconnects++;
	if(fileListDisconnects > SETTING(ACCEPTED_DISCONNECTS)) {
		// Pothead start svn61 (display cheat and stringdefs changes)
		sortCheatingString(STRING(DISCONNECTED_FILE_LIST), false);
		setCheat(STRING(DISCONNECTED_FILE_LIST) + " " + Util::toString(fileListDisconnects) + " " + STRING(TIMES), false, true);
		if(fileListDisconnects == SETTING(ACCEPTED_DISCONNECTS) + 1 && BOOLSETTING(DISPLAY_DISCONNECT_CHEAT))
			displayCheat( nick + " " + STRING(DISCONNECTED_FILE_LIST) + " " + Util::toString(fileListDisconnects) + " " + STRING(TIMES));
		// pothead end
		updated();
		sendRawCommand(SETTING(DISCONNECT_RAW), Client::CONTEXT_RAW_CONNECTION_TROUBLE);
		return true;
	}
	return false;
}
void User::connectionTimeout() {
	if(isSet(User::O_NICK)) {
		timeouts++;
		if(timeouts > 3) {
			{
				RLock l(cs);
				QueueManager::getInstance()->removeTestSUR(nick);
				hasTestSURinQueue = false;
				setTestSURoNickComplete();
				unsetFlag(User::O_NICK);
				// CDM EXTENSION BEGINS
				string chbc = ("CZDC/BCDC " + STRING(BASED_CLIENTS));
				// Pothead start svn61 (display cheat and stringdefs changes)
				setClientType(chbc);
				//Sulan was here and added the if...
				if(SETTING(CONSIDER_CZDCBCDC_CHEAT)){
					setCheat(chbc, true, false);
					displayCheat( nick + chbc);
				}
				// Pothead end
				// CDM EXTENSION ENDS
			}
			updated();
			//return true;
		}
		//return false;
	} else if(isSet(User::R_NICK)) {
		RLock l(cs);
		QueueManager::getInstance()->removeTestSUR(nick);
		hasTestSURinQueue = false;
		setTestSURrNickComplete();
		unsetFlag(User::R_NICK);
		//return true;
	} else {
		connectionTimeouts++;
		if(connectionTimeouts > SETTING(ACCEPTED_TIMEOUTS)) {
			// CDM EXTENSION BEGINS
			string contime = STRING(CONNECTION_TIMEOUT);
			// pothead starts svn61 (stringdefs and display cheat change from if statement)
			//Sulan start svn99
			sortCheatingString(contime, false);
			setCheat(contime + " " + Util::toString(connectionTimeouts) + " times", false, true);
			if(connectionTimeouts == SETTING(ACCEPTED_TIMEOUTS) + 1 && BOOLSETTING(DISPLAY_TIMEOUT_CHEAT))
				displayCheat(nick + " " + contime + " " + Util::toString(connectionTimeouts) + " " + STRING(TIMES));
			// pothead end
			// CDM EXTENSION ENDS
			updated();
			try {
				QueueManager::getInstance()->removeTestSUR(nick);
				User::Ptr user = this;
				QueueManager::getInstance()->removeFilelist(user);
			} catch(...) {
			}
			testSURComplete = testSURoNickComplete = testSURrNickComplete = filelistComplete = true;
			hasTestSURinQueue = hasFilelistinQueue = false;
			sendRawCommand(SETTING(TIMEOUT_RAW), Client::CONTEXT_RAW_CONNECTION_TROUBLE);
			//return true;
		}
		//return false;
	}
}
void User::setPassive() {
	setFlag(User::PASSIVE);
	if(tag.find(",M:A") != string::npos) {
		setCheat(STRING(TAG_STATES) + " " + STRING(ACTIVE_USING_PASSIVE), false, false);
		if (BOOLSETTING(DISPLAY_ACTIVE_USING_PASSIVE))
			displayCheat(nick + " " + STRING(TAG_STATES) + " " + STRING(ACTIVE_USING_PASSIVE));
		updated();
		sendRawCommand(SETTING(ACTIVE_USING_PASSIVE_RAW), Client::CONTEXT_ACTIVE_USING_PASSIVE);
	}
}
void User::logDetect(bool successful) {
	if(!isSet(User::O_NICK) && !isSet(User::R_NICK)) { 
		if(successful) {
			SettingsManager::getInstance()->set(SettingsManager::TOTAL_DETECTS, SETTING(TOTAL_DETECTS) + 1);
		} else {
			SettingsManager::getInstance()->set(SettingsManager::TOTAL_FAILED_DETECTS, SETTING(TOTAL_FAILED_DETECTS) + 1);
		}
	}
	//if(client){
	//	RLock l(cs);
	//	client->clientInfo(client, nick + "\r\n" + getReport());
	//}
}

void User::setBadIsp(bool bad) {
	badIsp = bad;
	if(bad) {
		if (BOOLSETTING(DISPLAY_BAD_ISP_CHEAT))
			displayCheat(nick + " " + STRING(BAD_ISP));
		sendRawCommand(SETTING(BAD_ISP), Client::CONTEXT_RAW_BAD_ISP);
	}
}

void User::gotIp(bool uIp) {
	if(BOOLSETTING(ISP_CHECKING) && isp.empty()) {
		Isp::Ptr& i = ClientProfileManager::getInstance()->getISP(uIp ? userIp : ip);
		isp = i->getIsp();
		setBadIsp(i->getBad());
	}
}

// pothead fix starts #2 for Sulan 29
void User::displayCheat(const string& aLine) {
	if(BOOLSETTING(DISPLAY_CHEATS_IN_MAIN_CHAT))
		addLine("*** " + STRING(USER) + " " + aLine);
}
// pothead fix ends
// pothead starts svn189 (strong dc code) svn196 (sulan code)
void User::rmDCDetect() {
	if (tag == Util::emptyString)
		return;

		string rmDC = "rmDC++ 0.403D[1]";
	if (getTagVersion() != Util::emptyString){
		if (RegexpHandler::matchProfile(getTagVersion(), "^0.40([0123]){1}$") && getClaimsToBeDCPP() && getDetectRMDC403D1()) {
			string cheat = rmDC + " in DC++ " + getTagVersion() + " emulation mode";
			setClientType(rmDC);
			setCheat(cheat , true, false);
			if (BOOLSETTING(DISPLAY_RMDC_CHEAT))
				displayCheat(getNick() + " " + cheat);
			logDetect(true);
			sendRawCommand(SETTING(RMDC), Client::CONTEXT_RAW_GPL_BREAKING_SCUM);
		}
	}

	if (RegexpHandler::matchProfile(tag, "^<StrgDC\\+\\+ V:1.00 RC7") && getDetectRMDC403D1()) {
		string cheat = rmDC + " in StrongDC++ 1.00 RC7 emulation mode";
		setClientType(rmDC);
		setCheat(cheat , true, false);
		if (BOOLSETTING(DISPLAY_RMDC_CHEAT))
			displayCheat(getNick() + " " + cheat);
		logDetect(true);
		sendRawCommand(SETTING(RMDC), Client::CONTEXT_RAW_GPL_BREAKING_SCUM);
	}
}
// pothead ends
void User::myinfoDetect() {
	if (!protectedUser && client && client->getUseMyinfoDetect()){ //gabberworld new_svn32 (works 100% only if hub sends $OpList before $MyINFO)
		MyinfoDetector* de = new MyinfoDetector();
		de->runDetect(this);
		delete de;
	}//gabberworld ends
}

void User::setCheat(const string& aCheatDescription, bool aBadClient, bool aBadFilelist) {
	sortCheatingString(aCheatDescription, true);
	if (aBadClient)
		badClient = aBadClient;
	if (aBadFilelist)
		badFilelist = aBadFilelist;
};

void User::sortCheatingString(const string& aCheatDescription, const bool adding) {
	StringTokenizer<string> st(getCheatingString(), ';');
	StringList& sl = st.getTokens();
	string newCheat;
	for(StringIter i = sl.begin(); i != sl.end(); ++i) {
		if((*i).find(aCheatDescription) == string::npos)
			newCheat += *i + ";";
	}
	if (adding)
		newCheat += aCheatDescription + ";";
	cheatingString = newCheat;
}

string User::getTagVersion() {
	string theVersion;
	if (tag.find("V:") != string::npos && tag.find(",M") != string::npos)
		theVersion = tag.substr(tag.find("V:") + 2, tag.find(",M") - tag.find("V:") - 2);
	theVersion = Util::isFloat(theVersion) ? theVersion : Util::emptyString;
	return theVersion;
}

void User::setCountry(){
	if (getCountryCode().empty() && !getHisIp().empty()) {
		setCountryCode(Util::getIpCountry(getHisIp()));
		if(client && BOOLSETTING(CHECK_MAX_IPS_PER_HUB)) {
			int maxConns = SETTING(MAX_CONNECTION_SETTING);
			if (maxConns < 10) 
				maxConns = 10;
			if (client->countExistingIps(getHisIp())> maxConns) {
				if (SETTING(DISPLAY_TO_MANY_IPS_CHEAT)) {
					setCheat("The qoute on IP: " + getHisIp() + " is full", true, false);
					displayCheat(getNick() + " has an ip that has exceeded the maximum amount of connections alowed");
				}
				if (SETTING(TO_MANY_IPS_RAW)){
					sendRawCommand(SETTING(TO_MANY_IPS_RAW), Client::CONTEXT_RAW_POSSIBLE_FLOODER);
				}
			}
		}

	}
}

const string User::changeCheatingDescriptionsTokens() {
	StringTokenizer<string> st(getCheatingString(), ';');
	StringList& sl = st.getTokens();
	string newCheat;
	for(StringIter i = sl.begin(); i != sl.end(); ++i) {
		if (newCheat == "")
			newCheat = *i;
		else
			newCheat += Util::numberToToken(SETTING(TOKEN_FOR_CHEAT_STRING)) + *i ;
	}
	return newCheat;
}
// CDM EXTENSION ENDS
/**
 * @file
 * $Id: User.cpp Last changed: 2005-07-14 13:05:46 By: sulan for SVN: 247 Exp$
 */